﻿using System;
using System.Collections;
using System.Collections.Generic;

using BizElements.Core;

namespace BizElements.BusinessLayer
{
    /// <summary>Utility/facade class that simplifies primary key serialization logic.</summary>
    public static class PrimaryKeySerializer
    {
        #region Configuration.

        static Dictionary<string, IPrimaryKeySerializer> SerializerRegistry = new Dictionary<string, IPrimaryKeySerializer>();
        static readonly IPrimaryKeySerializer DefaultSerializer = new PrimaryKeyConcatenator();

        /// <summary>Gets primary key serializer configured for the specfied table.</summary>
        /// <param name="tableName">Table name.</param>
        /// <returns>Primary key serializer.</returns>
        public static IPrimaryKeySerializer GetSerializer(string tableName)
        {
            IPrimaryKeySerializer serializer;
            SerializerRegistry.TryGetValue(tableName, out serializer);
            return serializer ?? DefaultSerializer;
        }

        /// <summary>Register custom serializer for the specified table.</summary>
        /// <param name="tableName">Table name.</param>
        /// <param name="serializer">Serializer.</param>
        public static void RegisterSerializer(string tableName, IPrimaryKeySerializer serializer)
        {
            SerializerRegistry[tableName] = serializer;
        }

        #endregion

        #region Facade.

        /// <summary>Extracts and serializes the specified entity's primary key.</summary>
        /// <param name="entity">Entity whose primary key is to be serialized.</param>
        /// <returns>Serialized primary key.</returns>
        public static string Serialize(IEntity entity)
        {
            return GetSerializer(entity.Table.TableName).Serialize(entity);
        }

        /// <summary>Extracts and serializes primary key of all entities in the specified collection.</summary>
        /// <param name="table">Table to which the entities belong.</param>
        /// <param name="entities">Entities whose primary key are to be serialized.</param>
        /// <returns>Array of serialized primary keys.</returns>
        public static string[] Serialize(IDbTable table, IEnumerable entities)
        {
            return GetSerializer(table.TableName).Serialize(entities);
        }

        /// <summary>Extracts and serializes primary key of all entities in the specified collection.</summary>
        /// <typeparam name="TEntity">Type of entities stored in the collection.</typeparam>
        /// <param name="entities">Entities whose primary key are to be serialized.</param>
        /// <returns>Array of serialized primary keys.</returns>
        public static string[] Serialize<TEntity>(IEnumerable<TEntity> entities)
            where TEntity : IEntity, new()
        {
            IDbTable table = new TEntity().Table;
            return GetSerializer(table.TableName).Serialize(entities);
        }

        /// <summary>Parses primary key from the specified string and sets the provided entity's primary key properties.</summary>
        /// <param name="entity">Entity whose primary key properties are to be deserialized and set.</param>
        /// <param name="serializedPrimaryKey">Serialized primary key.</param>
        public static void Deserialize(IEntity entity, string serializedPrimaryKey)
        {
            GetSerializer(entity.Table.TableName).Deserialize(entity, serializedPrimaryKey);
        }

        /// <summary>Parses primary keys from the specified string and creates entities whose primary key properties will be set.</summary>
        /// <typeparam name="TEntity">Entity class.</typeparam>
        /// <typeparam name="TTable">Metadata class for table that contains the specified entities.</typeparam>
        /// <param name="serializedPrimaryKeys">Serialized primary keys.</param>
        /// <returns>Entities whose primary key properties have been initialized with values parsed from the provided strings.</returns>
        public static EntityCollection<TEntity, TTable> Deserialize<TEntity, TTable>(IEnumerable<string> serializedPrimaryKeys)
            where TEntity : class, IEntity, new()
            where TTable : IDbTable, new()
        {
            EntityCollection<TEntity, TTable> entitiesWithPk = new EntityCollection<TEntity, TTable>();
            IPrimaryKeySerializer pkSerializer = GetSerializer(entitiesWithPk.IDbTable.TableName);
            foreach (string serializedPK in serializedPrimaryKeys)
            {
                TEntity entity = new TEntity();
                Deserialize(entity, serializedPK);
                entitiesWithPk.Add(entity);
            }

            return entitiesWithPk;
        }

        /// <summary>Parses primary keys from the specified string and creates entities whose primary key properties will be set.</summary>
        /// <param name="table">Metadata class for table that contains the specified entities.</param>
        /// <param name="serializedPrimaryKeys">Serialized primary keys.</param>
        /// <param name="destinationCollection">Collection to wich entities with PK will be added. No other properties, other than PK fields, will be set.</param>
        public static void Deserialize(IDbTable table, IEnumerable<string> serializedPrimaryKeys, IList destinationCollection)
        {
            IPrimaryKeySerializer pkSerializer = GetSerializer(table.TableName);
            foreach (string serializedPK in serializedPrimaryKeys)
            {
                IEntity entity = table.NewEntity();
                Deserialize(entity, serializedPK);
                destinationCollection.Add(entity);
            }
        }

        /// <summary>Parses primary keys from the specified string and creates entities whose primary key properties will be set.</summary>
        /// <typeparam name="TEntity">Entity class.</typeparam>
        /// <param name="serializedPrimaryKeys">Serialized primary keys.</param>
        /// <param name="destinationCollection">Collection to wich entities with PK will be added. No other properties, other than PK fields, will be set.</param>
        public static void Deserialize<TEntity>(IEnumerable<string> serializedPrimaryKeys, IList destinationCollection)
            where TEntity : IEntity, new()
        {
            IDbTable table = new TEntity().Table;
            IPrimaryKeySerializer pkSerializer = GetSerializer(table.TableName);
            foreach (string serializedPK in serializedPrimaryKeys)
            {
                IEntity entity = table.NewEntity();
                Deserialize(entity, serializedPK);
                destinationCollection.Add(entity);
            }
        }

        #endregion
    }
}